<?php

class Parser
{
    public $_config;
    public $_converterVal;
    public $_packs   = array();
    public $_da      = array();
    public $_docs    = array();
    public $_tags    = array( 'tags'  => array(), 'docs'  => array() );
    public $_subNodes     = array();
    public $sort     = 0;

    public function setConfig($config)
    {
        $this->_config = $config;
    }

    public function setConverterVal($converterVal)
    {
        $this->_converterVal = $converterVal;
    }

    public function run()
    {
        $this->_searchPacks();

        foreach ($this->_packs as $pack)
        {
            $packDir = $this->_unpack($pack);
            $documents = $this->_searchDocuments($packDir);
            $rubrica = $this->_getRubric($packDir);

            foreach ($documents as $document)
            {
                $this->_docs[] = $this->_parseDocument($document, $rubrica);
            }

            $this->_buildDaStract($packDir);
        }

        $this->_deleteEmptyNode();
        $this->_updateDocumentsCategory();
    }

    public function getTags()
    {
        return $this->_tags;
    }

    public function getDocs()
    {
        return $this->_docs;
    }

    public function getDa()
    {
        return $this->_da;
    }

    protected function _updateDocumentsCategory()
    {
        foreach ($this->_docs as $document)
        {
            foreach($this->_da['Documents'] as $daDocument)
            {
                if ($document->id == $daDocument->id)
                {
                    $document->rubrica_id = $daDocument->rubricId;
                }
            }
        }
    }

    public function clear()
    {
        $tmp_dir        = $this->_config['tmp_dir'];
        $pack_dir       = $this->_config['pack_dir'];
        $system_mode    = $this->_config['system_mode'];

        //delete tmp
        if ($objs = glob($tmp_dir."/*"))
        {
            foreach($objs as $obj)
            {
                $this->_removeDirectory($obj);
            }
        }

        //delete packs
        if ($system_mode != 'dev')
        {
            foreach ($this->_packs as $pack)
            {
                unlink($pack_dir . $pack);
            }
        }
    }

    protected function _getRubric($pack)
    {
        $da = file_get_contents($pack . 'DA/da.xml');
        $xml = new SimpleXMLElement($da);

        return array('id' => $xml['id'], 'name' => $xml['title']);
    }

    protected function _parseDocument($document, $rubrica)
    {
        $file = file_get_contents($document);
        $xml = new SimpleXMLElement($file);

        $docName = '';
        $docKeywords = '';
        $docUpdated = (string)$xml->метаданные->material['created_at']; //(string)$xml->метаданные->material['updated_at'];

        foreach ($xml->метаданные->material->attributes->attribute as $attribute)
        {
            if ((string)$attribute['title'] == "Полное название")
            {
                $docName = $attribute->value;
            }

            if ((string)$attribute['title'] == "Ключевые слова")
            {
                $docKeywords = $attribute->value;
            }
        }

        foreach ($xml->метаданные->material->attributes->object as $object)
        {
            if ((string)$object['title'] == "Редакции")
            {
                if (isset($object->instances->instance->attribute))
                {
                    foreach ($object->instances->instance->attribute as $attribute)
                    {
                        if ((string)$attribute['attr'] == "version_date")
                        {
                            $docUpdated = $attribute->value;
                        }
                    }
                }
            }
        }

        $text = $this->_convertText($xml);

        $doc = new Object_Document;
        $doc
            ->setId((string)$xml->метаданные->material['id'])
            ->setRubrica($rubrica)
            ->setApproveDate((string)$xml->метаданные->material['created_at'])      // temporary
            ->setUpdateDate((string)$docUpdated)
            ->setKeywords((string)$docKeywords)
            ->setName((string)$docName)
            ->setText((array)$text)
        ;

        return $doc;

    }

    protected function _convertText(SimpleXMLElement $xml)
    {
        $docNotify      = $xml->нотификация->asxml();

        $docHeader      = $xml->шапка_документа->asxml();
        $docChanging    = $xml->изменяющие_документы->asxml();
        $docBody        = $xml->текст_документа->asxml();
        $docSign        = $xml->подпись_документа->asxml();

        $docAttachHead      = isset($xml->вложение) ? $xml->вложение->шапка_вложения->asxml() : '';
        $docAttachChanging  = isset($xml->вложение) ? $xml->вложение->изменяющие_документы->asxml() : '';
        $docAttachText      = isset($xml->вложение) ? $xml->вложение->текст_вложения->asxml() : '';
        $docAttachXml       = $docAttachHead . $docAttachChanging . $docAttachText;
        $docAnnex           = isset($xml->приложение) ? $xml->приложение : '';

        //$docAnnexs      = isset($xml->приложение) ? $xml->приложение : array();

        //apply xml fix
        $fix = array(
            'from'  => array("\n", "\t", "  ", "> ", "< " ),
            'to'    => array(""  , ""  , " " , ">" , "<"  ),
        );

        $docNotify          = str_replace($fix['from'], $fix['to'], $docNotify);
        $docHeader          = str_replace($fix['from'], $fix['to'], $docHeader);
        $docBody            = str_replace($fix['from'], $fix['to'], $docBody);
        $docSign            = str_replace($fix['from'], $fix['to'], $docSign);
        $docChanging        = str_replace($fix['from'], $fix['to'], $docChanging);
        $docAttachHead      = str_replace($fix['from'], $fix['to'], $docAttachHead);
        $docAttachChanging  = str_replace($fix['from'], $fix['to'], $docAttachChanging);
        $docAttachText      = str_replace($fix['from'], $fix['to'], $docAttachText);
        $docAttachXml       = str_replace($fix['from'], $fix['to'], $docAttachXml);

        $docNotify          =  preg_replace('/ {2,}/',' ',$docNotify);
        $docHeader          =  preg_replace('/ {2,}/',' ',$docHeader);
        $docBody            =  preg_replace('/ {2,}/',' ',$docBody);
        $docSign            =  preg_replace('/ {2,}/',' ',$docSign);
        $docChanging        =  preg_replace('/ {2,}/',' ',$docChanging);
        $docAttachHead      =  preg_replace('/ {2,}/',' ',$docAttachHead);
        $docAttachChanging  =  preg_replace('/ {2,}/',' ',$docAttachChanging);
        $docAttachText      =  preg_replace('/ {2,}/',' ',$docAttachText);
        $docAttachXml       =  preg_replace('/ {2,}/',' ',$docAttachXml);

        // Save xml source
        $docXml = $docNotify . $docHeader . $docChanging . $docBody . $docSign . $docAttachXml . $docAnnex;
		
		//parse files
		$docNotify 			= $this->_processAttachments($docNotify, $xml);
		$docHeader 			= $this->_processAttachments($docHeader, $xml);
		$docChanging 		= $this->_processAttachments($docChanging, $xml);
		$docAnnex 			= $this->_processAttachments($docAnnex, $xml);
		$docSign 			= $this->_processAttachments($docSign, $xml);
		$docAttachHead 		= $this->_processAttachments($docAttachHead, $xml);
		$docAttachChanging 	= $this->_processAttachments($docAttachChanging, $xml);
		$docAttachText 		= $this->_processAttachments($docAttachText, $xml);
		$docAttachChanging 	= $this->_processAttachments($docAttachChanging, $xml);
		$docAttachChanging 	= $this->_processAttachments($docAttachChanging, $xml);	

        // Parse Signature
        foreach($this->_converterVal['Signature'] as $signValFrom => $signValTo) {
            $docSign    = preg_replace("/{$signValFrom}/is","$signValTo", $docSign);
        }

        // Parse Headers
        foreach($this->_converterVal['HearOnly'] as $headerValFrom => $headerValTo) {
            $docHeader      = preg_replace("/{$headerValFrom}/is","$headerValTo", $docHeader);
            $docAttachHead  = preg_replace("/{$headerValFrom}/is","$headerValTo", $docAttachHead);
        }

        // Parse Body
        foreach($this->_converterVal['BodyOnly'] as $bodyValFrom => $bodyValTo) {
            $docNotify      = preg_replace("/{$bodyValFrom}/is","$bodyValTo", $docNotify);
            $docBody        = preg_replace("/{$bodyValFrom}/is","$bodyValTo", $docBody);
            $docSign        = preg_replace("/{$bodyValFrom}/is","$bodyValTo", $docSign);
            $docChanging    = preg_replace("/{$bodyValFrom}/is","$bodyValTo", $docChanging);
            $docAttachChanging    = preg_replace("/{$bodyValFrom}/is","$bodyValTo", $docAttachChanging);
            $docAttachText  = preg_replace("/{$bodyValFrom}/is","$bodyValTo", $docAttachText);
        }

        // Parse Headers and Body
        foreach($this->_converterVal['All'] as $allValFrom => $allValTo) {
            $docNotify      = preg_replace("/{$allValFrom}/is","$allValTo", $docNotify);
            $docHeader      = preg_replace("/{$allValFrom}/is","$allValTo", $docHeader);
            $docBody        = preg_replace("/{$allValFrom}/is","$allValTo", $docBody);
            $docSign        = preg_replace("/{$allValFrom}/is","$allValTo", $docSign);
            $docChanging    = preg_replace("/{$allValFrom}/is","$allValTo", $docChanging);
            $docAttachHead  = preg_replace("/{$allValFrom}/is","$allValTo", $docAttachHead);
            $docAttachChanging  = preg_replace("/{$allValFrom}/is","$allValTo", $docAttachChanging);
            $docAttachText  = preg_replace("/{$allValFrom}/is","$allValTo", $docAttachText);
        }

        $docAttach = '';

        //parse Annex
        if ($xml->приложение)
        {
            foreach($xml->приложение->вложение as $attach)
            {
                $attachHead     = $attach->шапка_вложения->asxml();
                $attachChanging = $attach->изменяющие_документы->asxml();
                $attachBody     = $attach->текст_вложения->asxml();
                $attachSign     = $attach->подпись_вложения->asxml();

                //fix
                $attachHead     = str_replace($fix['from'], $fix['to'], $attachHead);
                $attachChanging = str_replace($fix['from'], $fix['to'], $attachChanging);
                $attachBody     = str_replace($fix['from'], $fix['to'], $attachBody);
                $attachSign     = str_replace($fix['from'], $fix['to'], $attachSign);
                $attachHead     =  preg_replace('/ {2,}/',' ',$attachHead);
                $attachBody     =  preg_replace('/ {2,}/',' ',$attachBody);
                $attachSign     =  preg_replace('/ {2,}/',' ',$attachSign);
				
				$attachHead 	= $this->_processAttachments($attachHead, $xml);
				$attachChanging = $this->_processAttachments($attachChanging, $xml);
				$attachBody 	= $this->_processAttachments($attachBody, $xml);
				$attachSign 	= $this->_processAttachments($attachSign, $xml);

                // Parse Signature
                foreach($this->_converterVal['Signature'] as $signValFrom => $signValTo) {
                    $attachSign    = preg_replace("/{$signValFrom}/is","$signValTo", $attachSign);
                    $attachBody    = preg_replace("/{$signValFrom}/is","$signValTo", $attachBody);
                }

                // Parse Headers
                foreach($this->_converterVal['HearOnly'] as $headerValFrom => $headerValTo) {
                    $attachHead  = preg_replace("/{$headerValFrom}/is","$headerValTo", $attachHead);
                }

                // Parse Body
                foreach($this->_converterVal['BodyOnly'] as $bodyValFrom => $bodyValTo) {
                    $attachChanging  = preg_replace("/{$bodyValFrom}/is","$bodyValTo", $attachChanging);
                    $attachBody      = preg_replace("/{$bodyValFrom}/is","$bodyValTo", $attachBody);
                    $attachSign      = preg_replace("/{$bodyValFrom}/is","$bodyValTo", $attachSign);
                }

                // Parse Headers and Body
                foreach($this->_converterVal['All'] as $allValFrom => $allValTo) {
                    $attachHead     = preg_replace("/{$allValFrom}/is","$allValTo", $attachHead);
                    $attachChanging = preg_replace("/{$allValFrom}/is","$allValTo", $attachChanging);
                    $attachBody     = preg_replace("/{$allValFrom}/is","$allValTo", $attachBody);
                    $attachSign     = preg_replace("/{$allValFrom}/is","$allValTo", $attachSign);
                }

                $docAttach .= '<p style="text-align: center;"><span style="color: #c0c0c0;">_____________________________________________________________________________________________________________</span></p>'
							 . $attachHead . $attachChanging . $attachBody . $attachSign
							 . '</p>';
            }

            if ($docAttach != '')
                $docAttach = '<p>ДОДАТКИ</p>' . $docAttach;
        }

		if ($docAttachHead != '' OR $docAttachChanging != '' OR $docAttachText != '')
			{
				$docAttach = '<p style="text-align: center;"><span style="color: #c0c0c0;">_____________________________________________________________________________________________________________</span></p>'
							 . $docAttachHead . $docAttachChanging . $docAttachText
							 . '</p>' . $docAttach;
			}
		/*
        if ((string)$docNotify != '')
        {
			var_dump($docNotify); exit;
            //$docNotify = '<p><span style="background-color: #ffff99;">' . $docNotify . '</span></p>';
        }
		*/

        $htmlText = $docNotify . $docHeader . $docChanging . $docBody . $docSign . $docAttach;
			//$docAttachHead . $docAttachChanging . $docAttachText . 
			
        $htmlText = $this->_processImg($htmlText, $xml);

        //search all xml tags after convert process
        if (preg_match_all('/<[а-яА-Я\_]+/u', $htmlText, $tags))
        {
            $tagsArr = array();

            foreach($tags as $tag)
            {
                $t = str_replace('<', '', $tag[0]);
                $this->_tags['tags'][] = $t;
                $tagsArr[] = $t;
            }

            $this->_tags['docs'][] = array(
                'DocId' => (string)$xml->метаданные->material['id'],
                'tags'  => implode(', ', $tagsArr),
            );
        }

        $this->_tags['tags'] = array_unique($this->_tags['tags']);

        return array(
            //'xml'   => $docXml,
            'html'  => $htmlText,

            // for dev only
            //'sign'      => $docSign,
            //'changing'  => $docChanging,
            //'attach'    => $docAttachHead . $docAttachText . $docAttach,
        );
    }

    protected function _processAttachments($text, $xml)
    {
        $attachments = (isset($xml->метаданные->material->files->attachment) ? $xml->метаданные->material->files->attachment : array());

        foreach($attachments as $attachment)
        {
            if ($attachment['ext'] != 'docx')
                continue;

            $attachId = $attachment['id'];
            list($fileName, $fileExt) = explode('.', $attachment['title']);
            $linkId = $this->_getLinkIdByName($fileName, $xml);

            if ($linkId)
            {
                $text = preg_replace(
                    "/<метка id=\"{$linkId}\">(.*?)<\/метка>/is",
                    "<a href=\"/upload/regulations/attachment_{$attachId}.docx\">\\1</a>",
                $text);
            }
        }

        return $text;
    }

    protected function _processImg($text, $xml)
    {
        if (preg_match_all('/<в_метка id="\d+">\s*?(.*?)\s*?<\/в_метка>/', $text, $labels))
        {
            $replace = array('from' => array(), 'to' => array());

            foreach($labels[1] as $key => $label)
            {
                $str                = $labels[0][$key];
                $label              = trim($label);
                $attachId           = $this->_getAttachIdByLabel($label, $xml);

                $replace['from'][]  = $str;
                $replace['to'][]    = $this->_getAttachImg($attachId);
            }

            $text = str_replace($replace['from'], $replace['to'], $text);
        }

        return $text;
    }

    protected function _getLinkIdByName($name, $xml)
    {
        foreach ($xml->ссылки->link as $link)
        {
            if ($link->ссылка->описание == $name)
            {
                $source = $link->ссылка->источник;
                if (trim((string)$source['элемент'] != ''))
                    return $source['элемент'];
            }
        }

        return false;
    }

    protected function _getLabelId($label, $xml)
    {
        $links = $xml->ссылки->link;

        foreach($links as $link)
        {
            if (trim((string)$link->в_ссылка->описание) == $label)
            {
                return (string)$link->в_ссылка->цель['документ'];
            }
        }
    }

    protected function _getAttachIdByLabel($label, $xml)
    {
        if (!$xml->метаданные->material->files)
            return false;

        foreach($xml->метаданные->material->files->attachment as $attach)
        {
            $filename = strtolower($label . '.png');
            $attachName = strtolower((string)$attach['title']);

            if ( $filename == $attachName )
            {
                return (string)$attach['id'];
            }
        }

        return false;
    }

    protected function _getAttachImg($attachId)
    {
        $imgPath = $this->_config['img_path'];
        $imgPath = str_replace('%%img_id%%', $attachId, $imgPath);

        return "<img src=\"{$imgPath}\" />";
    }

    protected function _searchPacks()
    {
        $files = scandir($this->_config['pack_dir']);

        foreach ($files as $file)
        {
            if ($this->_getExtension($file) == "zip")
            {
                $this->_packs[] = $file;
                break;
            }
        }
    }

    protected function _buildDaStract($pack)
    {
        $daFile = $pack . 'DA/da.xml';
        $sort = 0;

        $file = file_get_contents($daFile);
        $xml = new SimpleXMLElement($file);

        $da = new Object_Da_Rubric();
        $da->setId($xml['id']);
        $da->setTitle($xml['title']);
        $da->setSort($sort);

        $this->_da['Rubrics'][] = $da;
        $this->_buildDocStract($xml);

        $this->_buildDaNode($xml);
    }

    protected function _buildDaNode($xml)
    {
        foreach($xml->node as $xmlNode)
        {
            $this->sort++;

            $da = new Object_Da_Rubric();
            $da->setId($xmlNode['id']);
            $da->setTitle($xmlNode['title']);
            $da->setParentId($xml['id']);
            $da->setSort($this->sort);

            $catAdded = false;

            if (isset($xmlNode->document))
            {
                $this->_da['Rubrics'][] = $da;
                $catAdded = true;
                $this->_buildDocStract($xmlNode);
            }

            if (isset($xmlNode->node))
            {
                if (!$catAdded) $this->_da['Rubrics'][] = $da;
                $this->_buildDaNode($xmlNode);
            }
        }
    }

    protected function _deleteEmptyNode()
    {
        foreach ($this->_da['Rubrics'] as $node)
        {
            if ($node->parentId == 0)
                $this->_buildSubNodes($node);
        }

        $rubrics = array();

        foreach($this->_da['Rubrics'] as $node)
        {
            $needDel = false;

            foreach($this->_subNodes as $subNode)
            {
                if ($node->id == $subNode->id)
                {
                    $needDel = true;
                }
            }

            if (!$needDel)
                $rubrics[] = $node;
        }

        $this->_da['Rubrics'] = $rubrics;
        unset($rubrics);
    }

    protected function _buildSubNodes($node)
    {
        foreach ($this->_da['Rubrics'] as $subNode)
        {
            if ($subNode->parentId == $node->id)
            {
                if ($this->_isHaveSubNodes($subNode))
                {
                    $this->_buildSubNodes($subNode);
                }
                else
                {
                    if (!$this->_isHaveNodeDocs($subNode))
                        $this->_subNodes[] = $subNode;
                }
            }
        }
    }

    protected function _isHaveSubNodes($node)
    {
        foreach ($this->_da['Rubrics'] as $subNode)
        {
            if ($subNode->parentId == $node->id)
            {
                return true;
            }
        }

        return false;
    }

    protected function _isHaveNodeDocs($node)
    {
        foreach ($this->_da['Documents'] as $doc)
        {
            if ($doc->rubricId == $node->id)
                return true;
        }

        return false;
    }

    protected function _buildDocStract($Rubric)
    {
        $sort = 0;

        foreach ($Rubric->document as $xmlDocument)
        {
            $document = new Object_Da_Document();
            $document->setId($xmlDocument['id']);
            $document->setTitle($xmlDocument['title']);
            $document->setRubricId($Rubric['id']);
            $document->setSort($sort);

            $this->_da['Documents'][] = $document;
            $sort++;
        }
    }

    protected function _searchDocuments($pack)
    {
        $documents = array();

        $dirName = $pack . 'documents/';
        $dir = opendir($dirName);

        while (($file = readdir($dir)) !== false)
        {
            if($file != "." && $file != "..")
            {
                if(is_dir($dirName . $file))
                {
                    $documents[] = $dirName . $file . '/' . $file . '.xml';
                }
            }
        }

        return $documents;
    }

    protected function _unpack($packName) {
        require_once('pclzip-2-8-2/pclzip.lib.php');
        $archive = new PclZip($this->_config['pack_dir'] . $packName);

        if ($archive->extract($this->_config['tmp_dir']) == 0) {
            die("Error : ".$archive->errorInfo(true));
        }
        return $this->_config['tmp_dir'] . str_replace('.zip', '', $packName) . '/';
    }

    protected function _getExtension($filename) {
        return strtolower(end(explode(".", $filename)));
    }

    protected function _removeDirectory($dir) {
        if ($objs = glob($dir."/*"))
        {
            foreach($objs as $obj)
            {
                is_dir($obj) ? $this->_removeDirectory($obj) : unlink($obj);
            }
        }

        rmdir($dir);
    }

    // public function setToFile ($documents,$nameRubric){

    //     $file = fopen($nameRubric, "a");
    //     foreach ($documents as $document) {
           
    //         fwrite($file, $document->id." ");
    //         fwrite($file, $document->name." ");
    //         fwrite($file, $document->rubrica_id." ");
    //         fwrite($file, $document->rubrica_name." ");
    //         fwrite($file, $document->title." ");
    //         fwrite($file, $document->parentId." ");
    //         fwrite($file, $document->rubricId." ");
    //         fwrite($file, $document->approve_date." ");
    //         fwrite($file, $document->updated_date." ");
    //         fwrite($file, $document->text['html']." ");
    //         fwrite($file, $document->sort." ");
    //     }
    //             fclose($file);
    // }

}